﻿using System;
using System.Collections.Generic;

using iStudio.Structure.Message;

namespace iStudio.Manager.Setting
{
    public sealed class SettingManager : IObservable<SettingMessage>
    {
        private readonly List<IObserver<SettingMessage>> _observers;
        private readonly Dictionary<string, object> _settingValues;

        internal SettingManager()
        {
            _observers = new List<IObserver<SettingMessage>>();
            _settingValues = new Dictionary<string, object>();
        }

        public IDisposable Subscribe (IObserver<SettingMessage> observer)
        {
            if (!_observers.Contains (observer))
            {
                _observers.Add (observer);
            }
            return new Unsubscriber (_observers, observer);
        }

        public void ChangeSetting (string id, object value)
        {
            ChangeSetting(new SettingMessage (id, value));
        }

        public void ChangeSetting (SettingMessage item)
        {
            //
            // Check if item Id is not null
            //

            if (String.IsNullOrEmpty (item.Id))
            {
                return;
            }

            //
            // Check if setting is registered, if it is not we 
            //

            if (_settingValues.ContainsKey (item.Id))
            {
                throw new ArgumentException("Setting ID not defined");
            }

            //
            // Alert all observers
            //

            foreach (var observer in _observers)
            {
                observer.OnNext(item);
            }
        }

        public void RegisterSetting (string id, object value)
        {
            RegisterSetting (new SettingMessage (id, value));
        }

        public void RegisterSetting (SettingMessage item)
        {
            if (String.IsNullOrEmpty (item.Id))
            {
                throw new ArgumentException("Setting ID must be unique key.");
            }

            if (_settingValues.ContainsKey (item.Id))
            {
                throw new ArgumentException("Setting is already registed, use ChangeSetting method.");
            }

            _settingValues.Add (item.Id, item.Value);
        }

        public object GetSetting (string id)
        {
            if (_settingValues.ContainsKey (id))
            {
                return _settingValues[id];
            }

            return null;
        }

        public object GetSetting (SettingMessage item)
        {
            return GetSetting (item.Id);
        }

        public void EndTransmission()
        {
            foreach (var observer in _observers.ToArray())
            {
                if (_observers.Contains (observer))
                {
                    observer.OnCompleted ();
                }
            }

            _observers.Clear();
        }

        #region Unsubscriber class
        private class Unsubscriber : IDisposable
        {
            private readonly List<IObserver<SettingMessage>> _observers;
            private readonly IObserver<SettingMessage> _observer;

            public Unsubscriber(List<IObserver<SettingMessage>> observers, IObserver<SettingMessage> observer)
            {
                _observers = observers;
                _observer = observer;
            }

            public void Dispose()
            {
                if (_observer != null && _observers.Contains(_observer))
                {
                    _observers.Remove(_observer);
                }
            }
        }

        #endregion
    }
}
